package 加密与安全;

/*
ElGamal算法，是一种较为常见的加密算法，它是基于1985年提出的公钥密码体制和椭圆曲线加密体系。既能用于数据加密也能用于数字签名，其安全性
依赖于计算有限域上离散对数这一难题。在加密过程中，生成的密文长度是明文的两倍，且每次加密后都会在密文中生成一个随机数K，在密码中主要应用离
散对数问题的几个性质：求解离散对数（可能）是困难的，而其逆运算指数运算可以应用平方-乘的方法有效地计算。也就是说，在适当的群G中，指数函数
是单向函数。

特点：
1、在JDK里并没有提供对ElGamal算法的实现.而是通过BouncyCastle实现.
2、只提供公钥加密,私钥解密，不提供私钥加密，公钥解密
3、ElGamal算法支持数据加密与数字签名.
4、密钥长度:160~16384(8的倍数),默认的密钥长度为1024.

4.1 算法流程

1.乙方构建密钥对儿，将公钥公布给甲方，将私钥保留。
2.甲方使用公钥加密数据，然后用公钥对加密后的数据签名，发送给乙方签名以及加密后的数据；乙方使用私钥、签名来验证待解密数据是否有效，如果有效使用私钥对数据解密。

4.2 具体步骤
在使用之前需要为JDK添加新的Provider.

Security.addProvider(new BouncyCastleProvider());
RSA初始化密钥对是通过KeyPairGenerator实现的.而ElGamal初始化密钥对则是通过AlgorithmParameterGenerator实现的.
使用AlgorithmParameterGenerator的getInstance()静态方法获取AlgorithmParameterGenerator实例,参数是算法名.

AlgorithmParameterGenerator algorithmParameterGenerator = AlgorithmParameterGenerator.getInstance("Elgamal");
使用AlgorithmParameterGenerator的init()方法进行初始化密钥长度.

algorithmParameterGenerator.init(256);
JDK没有提供对ElGamal的支持，但是jce提供了构建秘钥对的方式DHParameterSpec，具体方式见代码

// 生成算法参数
AlgorithmParameters algorithmParameters = algorithmParameterGenerator.generateParameters();
// 构建参数材料
//JDK没有提供对ei的支持，但是jce框架提供了构建秘钥对的方式DHParameterSpec
DHParameterSpec dhParameterSpec = (DHParameterSpec)algorithmParameters.getParameterSpec(DHParameterSpec.class);
// 实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Elgamal");  
// 初始化密钥对生成器  
keyPairGenerator.initialize(dhParameterSpec, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
到这一步后就和RSA的步骤类似了，可参考上文或者查看实现代码。

4.3 实现代码
*/
import java.security.AlgorithmParameterGenerator;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.spec.DHParameterSpec;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * {@link http://www.cnblogs.com/allanzhang/}
 * 
 * @author 小卖铺的老爷爷
 *
 */
//public class ElGamalTest {
public class 非对称之ElGamal算法 {

	public static final String src = "laoyeye Elgamal test";

	public static void main(String[] args) {
		jdkElgamal();

	}

	// jdk实现：“公钥加密，私钥解密” ， 因为Elgamal不支持“私钥加密、公钥解密”。
	public static void jdkElgamal() {
		try {
			// 加入对BouncyCastle支持
			Security.addProvider(new BouncyCastleProvider());

			// 初始化密钥
			AlgorithmParameterGenerator algorithmParameterGenerator = AlgorithmParameterGenerator.getInstance("Elgamal");
			// 初始化参数生成器
			algorithmParameterGenerator.init(256);
			// 生成算法参数
			AlgorithmParameters algorithmParameters = algorithmParameterGenerator.generateParameters();
			// 构建参数材料
			// JDK没有提供对ei的支持，但是jce框架提供了构建秘钥对的方式DHParameterSpec
			DHParameterSpec dhParameterSpec = (DHParameterSpec) algorithmParameters.getParameterSpec(DHParameterSpec.class);
			// 实例化密钥对生成器
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Elgamal");
			// 初始化密钥对生成器
			keyPairGenerator.initialize(dhParameterSpec, new SecureRandom());
			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			// 公钥
			PublicKey elGamalPublicKey = keyPair.getPublic();
			// 私钥
			PrivateKey elGamalPrivateKey = keyPair.getPrivate();
			System.out.println("Public Key:" + Base64.encodeBase64String(elGamalPublicKey.getEncoded()));
			System.out.println("Private Key:" + Base64.encodeBase64String(elGamalPrivateKey.getEncoded()));
			// 初始化公钥
			// 密钥材料转换
			X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(elGamalPublicKey.getEncoded());
			// 实例化密钥工厂
			KeyFactory keyFactory = KeyFactory.getInstance("Elgamal");
			// 产生公钥
			PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec2);
			// 数据加密
			// Cipher cipher = Cipher.getInstance("Elgamal");
			Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, publicKey2);
			byte[] result2 = cipher.doFinal(src.getBytes());
			System.out.println("Elgamal ---- 加密:" + Base64.encodeBase64String(result2));
			// 数据解密
			PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(elGamalPrivateKey.getEncoded());
			keyFactory = KeyFactory.getInstance("Elgamal");
			PrivateKey privateKey5 = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
         //Cipher cipher5 = Cipher.getInstance("Elgamal");
			Cipher cipher5 = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher5.init(Cipher.DECRYPT_MODE, privateKey5);
			byte[] result5 = cipher5.doFinal(result2);
			System.out.println("Elgamal ---- 解密:" + new String(result5));
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
/*
 * 4.4 遇到的问题 代码报错：“Illegal key size or default
 * parameters”异常，是因为美国的出口限制，Sun通过权限文件（local_policy.jar、US_export_policy.jar）
 * 做了相应限制。因此存在一些问题。
 * 
 * Java 6 无政策限制文件：http://www.oracle.com/technetwork/java/javase/downloads/jce-6-
 * download-429243.html Java 7
 * 无政策限制文件：http://www.oracle.com/technetwork/java/javase/downloads/jce-7-
 * download-432124.html
 * 找到对应jdk的安装目录。D:\JDK1.6\jdk1.7.0_45\jre\lib\security目录下，对应覆盖local_policy.
 * jar和US_export_policy.jar两个jar包就可以了。
 * 
 * 补充：
 * 关于源码中PKCS8EncodedKeySpec，X509EncodedKeySpec密钥的转换，实际上并不需要转换，也不需要重新生成keyfactory
 * 。因为具体我也没应用过，可能是实际项目中一般会这样，因为实际接受方和发送方并不知道对方的密钥的encoded
 * format（编码格式），所以需要转换成自己使用encoded format。而KeyFactory就是转换格式后重新生成私钥，公钥。
 * 
 * 总结： 1、对称加密算法中使用的是密钥(SecretKey)，非对称加密算法中使用的是密钥对(公钥、私钥)。
 * 2、DH算法比较复杂，需要根据公钥生成自己的秘钥对，然后使用公钥和自己的私钥生成自己的本地秘钥在进行加密
 * 3、RSA相对来讲简单的多，支持公钥加密、私钥解密，私钥加密、公钥解密。
 * 4、ElGamal算法和RSA类似，但是只支持公钥加密,私钥解密，不支持私钥加密，公钥解密。
 */